import { ISA } from '@/atmosphere'
import { getEntity } from '../base'
import { getFlightPerformanceTable, type FlightPerformanceTable } from './flight-performance'
import type { AircraftType } from './types'

export interface ClimbProfileItem {
  alt: number
  dist: number
  time: number
}

export class ClimbProfile {
  readonly items: ClimbProfileItem[]
  readonly minAlt: number
  readonly maxAlt: number
  readonly altStep = ALT_STEP
  constructor(acType: AircraftType) {
    const performance = getFlightPerformanceTable(acType)
    this.items = makeClimbProfileItemList(performance)
    this.minAlt = this.items[0].alt
    this.maxAlt = this.items[this.items.length - 1].alt
  }

  public getItems(alt0: number, alt1: number): ClimbProfileItem[] {
    return cut(this.items, alt0, alt1)
  }
}

const climbProfiles: Record<string, ClimbProfile> = {}

export function getClimbProfileById(id: string): ClimbProfile | null {
  const acType = getEntity<AircraftType>('AircraftType', id)
  if (!acType) {
    return null
  }
  return getClimbProfile(acType)
}

export function getClimbProfile(type: AircraftType): ClimbProfile {
  let ret = climbProfiles[type.id]
  if (!ret) {
    ret = new ClimbProfile(type)
    if (ret) {
      climbProfiles[type.id] = ret
    }
  }
  return ret
}

const ALT_STEP = 100
const MAX_ALT = ALT_STEP * 120

function cut(lst: ClimbProfileItem[], alt0: number, alt1: number): ClimbProfileItem[] {
  alt0 = Math.max(alt0, lst[0].alt)
  alt1 = Math.min(alt1, lst[lst.length - 1].alt)
  if (alt0 >= alt1 || lst.length < 2) {
    return []
  }
  const ret: ClimbProfileItem[] = []
  const startIdx = Math.floor((alt0 - lst[0].alt) / ALT_STEP)
  for (let i = startIdx; i < lst.length; i++) {
    const it = lst[i]
    if (it.alt < alt0) {
      continue
    }
    if (it.alt > alt1) {
      if (i > 0) {
        const it1 = interpolate(lst[i - 1], it, alt1)
        ret.push(it1)
      }
      break
    }
    if (ret.length === 0) {
      if (it.alt > alt0 && i < lst.length) {
        const it1 = interpolate(it, lst[i + 1], alt0)
        ret.push(it1)
      }
    }
    ret.push({
      time: it.time,
      alt: it.alt,
      dist: it.dist
    })
    if (it.alt === alt1) {
      break
    }
  }
  if (ret.length === 0) {
    return ret
  }
  const dt = ret[0].time
  const dd = ret[0].dist
  for (const e of ret) {
    e.time -= dt
    e.dist -= dd
  }

  return ret
}

function interpolate(i0: ClimbProfileItem, i1: ClimbProfileItem, alt: number): ClimbProfileItem {
  if (alt === i0.alt) {
    return i0
  }
  if (alt === i1.alt) {
    return i1
  }
  const f = (alt - i0.alt) / (i1.alt - i0.alt)
  return {
    alt,
    dist: (i1.dist - i0.dist) * f + i0.dist,
    time: (i1.time - i0.time) * f + i0.time
  }
}

function makeClimbProfileItemList(table: FlightPerformanceTable): ClimbProfileItem[] {
  let preAlt = 0
  let preT = 0
  let preDist = 0
  let alt = 0
  let t = 0
  let dist = 0
  const ret: ClimbProfileItem[] = []
  ret.push({
    alt: 0,
    dist: 0,
    time: 0
  })
  let targetAlt = 100
  while (alt < MAX_ALT) {
    const p = table.getPerformance(alt)
    t++
    if (p.normClimbRate < 0.15) {
      break
    }
    alt += p.normClimbRate
    const tas = ISA.CAS2TAS(alt, p.normClimbIAS)
    dist += tas
    while (targetAlt <= alt) {
      const f = (targetAlt - preAlt) / (alt - preAlt)
      // console.log(f, preDist, dist, dist - preDist + preDist)
      ret.push({
        alt: targetAlt,
        dist: f * (dist - preDist) + preDist,
        time: f * (t - preT) + preT
      })
      targetAlt += 100
    }
    preAlt = alt
    preDist = dist
    preT = t
  }

  return ret
}
